﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using Microsoft.Win32;
using System.Diagnostics;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Drawing;
using Newtonsoft.Json.Serialization;
namespace VersionManage.Logic
{

    public class WebLogic
    {
        public static HttpResult<R> GetHttpResult<R>(HttpItem item)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            // 准备请求...
            try
            {
                //获取Request
                request = CreateRequest(item);

                using (response = request.GetResponse() as HttpWebResponse)
                {
                    return new HttpResult<R>(response);
                }
            }
            catch (WebException e)
            {
                if (e.Response != null)
                {
                    response = (HttpWebResponse)e.Response;
                    HttpResult<R> result = new HttpResult<R>(response);
                    response.Close();
                    return result;
                }
                return new HttpResult<R>(false, null, e.Message);
            }
        }

        private static HttpWebRequest CreateRequest(HttpItem item)
        {
            HttpWebRequest request = WebRequest.Create(item.Url) as HttpWebRequest;
            request.Method = item.Method.ToString();
            request.ContentType = item.ContentType;
            request.Date = item.Date;
            //request.ra
            if (item.Headers != null)
            {
                foreach (var head in item.Headers)
                {
                    if (head.StartsWith("DATE:"))
                        request.Date = DateTime.Parse(head.Substring(head.IndexOf(":") + 2));
                    else if (head.StartsWith("Content-Type:"))
                        request.ContentType = head.Substring(head.IndexOf(":") + 2);
                    else if (head.StartsWith("Range:"))
                    {
                        string[] spli = head.Split('-');
                        request.AddRange(Convert.ToInt32(spli[0].Remove(0, 13)), Convert.ToInt32(spli[1]));
                    }
                    else
                        request.Headers.Add(head);
                }
            }

            if (item.Content != null && item.Content.Length > 0)
            {
                request.ContentLength = item.Content.Length;
                Stream outstream = request.GetRequestStream();
                outstream.Write(item.Content, 0, item.Content.Length);
                outstream.Close();
            }
            return request;
        }

        static WebLogic()
        {
            ServicePointManager.SecurityProtocol |= (SecurityProtocolType)768 | (SecurityProtocolType)3072;
        }
    }

    /// <summary>
    /// Http请求参数
    /// </summary>
    public class HttpItem
    {
        public HttpItem()
        {
            Headers = new List<string>();
            Headers.Add(string.Format("DATE: {0}", DateTime.UtcNow.ToString("R")));
        }


        /// <summary>
        /// 是否取消当前请求
        /// </summary>
        public bool Cancel
        {
            get;
            set;
        }

        private DateTime date;
        public DateTime Date
        {
            get
            {
                //foreach (var item in Headers)
                //    if (item.StartsWith("DATE:"))
                //        return date;
                //date = DateTime.UtcNow;
                //Headers.Add(string.Format("DATE: {0}", date.ToString("R")));
                return date;
            }
            set
            {
                date = value;
                //Headers.RemoveAll(o => o.StartsWith("DATE:"));
                //Headers.Add(string.Format("DATE: {0}", date.ToString("R")));
            }
        }
        public string Url { get; set; }
        /// <summary>
        /// 请求方法  post,get,put delete等
        /// </summary>
        public HttpMethod Method { get; set; }
        public List<string> Headers { get; set; }

        public Dictionary<string, string> HeadersToDictionary()
        {
            if (Headers.Count == 0)
                return null;
            Dictionary<string, string> dirc = new Dictionary<string, string>();
            foreach (var item in Headers)
            {
                string key = item.Substring(0, item.IndexOf(":"));
                string value = item.Substring(key.Length + 1);
                if (dirc.ContainsKey(key))
                    dirc.Remove(key);
                dirc.Add(key, value);
            }
            return dirc;
        }

        /// <summary>
        /// 如果ContentType赋值,它会覆盖Headers中的conentType
        /// </summary>
        public string ContentType
        {
            get
            {
                foreach (var item in Headers)
                    if (item.StartsWith("Content-Type:"))
                        return item.Substring(item.IndexOf(":") + 2);
                return null;
            }
            set
            {
                Headers.RemoveAll(o => o.StartsWith("DATE:"));
                Headers.Add(string.Format("Content-Type: {0}", value));
            }

        }

        /// <summary>
        /// 请求参数
        /// </summary>
        public object ContentObject { get; set; }


        int _Timeout = 100000;
        /// <summary>
        /// 默认请求超时时间
        /// </summary>
        public int Timeout
        {
            get { return _Timeout; }
            set { _Timeout = value; }
        }
        int _ReadWriteTimeout = 30000;


        /// <summary>
        /// 默认写入Post数据超时间
        /// </summary>
        public int ReadWriteTimeout
        {
            get { return _ReadWriteTimeout; }
            set { _ReadWriteTimeout = value; }
        }


        string _UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";
        /// <summary>
        /// 客户端访问信息默认Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
        /// </summary>
        public string UserAgent
        {
            get { return _UserAgent; }
            set { _UserAgent = value; }
        }

        CookieCollection cookiecollection = null;
        /// <summary>
        /// Cookie对象集合
        /// </summary>
        public CookieCollection CookieCollection
        {
            get { return cookiecollection; }
            set { cookiecollection = value; }
        }


        string _CerPath = string.Empty;
        /// <summary>
        /// 证书绝对路径
        /// </summary>
        public string CerPath
        {
            get { return _CerPath; }
            set { _CerPath = value; }
        }
        private Boolean isToLower = true;
        /// <summary>
        /// 是否设置为全文小写
        /// </summary>
        public Boolean IsToLower
        {
            get { return isToLower; }
            set { isToLower = value; }
        }
        private Boolean allowautoredirect = true;
        /// <summary>
        /// 支持跳转页面，查询结果将是跳转后的页面
        /// </summary>
        public Boolean Allowautoredirect
        {
            get { return allowautoredirect; }
            set { allowautoredirect = value; }
        }
        private int connectionlimit = 1024;
        /// <summary>
        /// 最大连接数
        /// </summary>
        public int Connectionlimit
        {
            get { return connectionlimit; }
            set { connectionlimit = value; }
        }
        private string proxyusername = string.Empty;
        /// <summary>
        /// 代理Proxy 服务器用户名
        /// </summary>
        public string ProxyUserName
        {
            get { return proxyusername; }
            set { proxyusername = value; }
        }
        private string proxypwd = string.Empty;
        /// <summary>
        /// 代理 服务器密码
        /// </summary>
        public string ProxyPwd
        {
            get { return proxypwd; }
            set { proxypwd = value; }
        }
        private string proxyip = string.Empty;
        /// <summary>
        /// 代理 服务IP
        /// </summary>
        public string ProxyIp
        {
            get { return proxyip; }
            set { proxyip = value; }
        }


        /// <summary>
        /// http body
        /// </summary>
        public byte[] Content { get; set; }

    }

    public enum HttpMethod
    {
        Get = 1,
        Post = 2,
        Put = 3,
        Head = 4,
        Delete = 5
    }

    public class HttpResult<R>
    {
        public int StatusCode { get; set; }
        /// <summary>
        /// 请求是否成功
        /// </summary>
        public bool Success
        {
            get;
            private set;
        }

        /// <summary>
        /// 返回值
        /// </summary>
        public R Result
        {
            get;
            private set;
        }

        /// <summary>
        /// 响应返回的原始值 
        /// </summary>
        public object Content
        {
            get;
            private set;
        }

        /// <summary>
        /// 服务器返回消息
        /// </summary>
        public string Message
        {
            get;
            private set;
        }

        public Dictionary<string, string> Headers
        {
            get;
            private set;
        }


        public HttpResult(bool success, string result, string msg)
        {
            Success = success;
            Message = msg;
            Content = result;
        }

        public HttpResult(HttpWebResponse response)
        {
            if (response == null)
                return;
            Message = response.StatusDescription;
            StatusCode = (int)response.StatusCode;

            if ((int)response.StatusCode >= 200 && (int)response.StatusCode < 300)
                Success = true;

            Headers = new Dictionary<string, string>();
            foreach (var item in response.Headers.AllKeys)
                Headers.Add(item, response.Headers[item]);
            string content = null;
            Stream myResponseStream = response.GetResponseStream();
            byte[] buffer = null;
            if (response.ContentLength > 0)
            {
                buffer = new byte[response.ContentLength];
                int offset = 0;
                int count = 0;
                int length = buffer.Length;
                do
                {
                    count = myResponseStream.Read(buffer, offset, length);
                    offset += count;
                    length -= count;
                } while (count > 0);
                Content = buffer;
                content = System.Text.UTF8Encoding.UTF8.GetString(buffer);
            }
            else
            {
                StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
                content = myStreamReader.ReadToEnd();
                myStreamReader.Close();
                myResponseStream.Close();
                Content = content;
            }

            try
            {
                string typename = typeof(R).Name;
                if (typename == "String")
                {
                    Result = (R)(object)content;
                    return;
                }
                if (typename == "Byte[]" && buffer != null)
                {
                    Result = (R)(object)buffer;
                    return;
                }
                if (!string.IsNullOrEmpty(content))
                {
                    if (typeof(R).IsValueType)
                        Result = (R)Convert.ChangeType(content, typeof(R));
                    else if (response.ContentType.Contains("application/json"))
                        Result = JsonConvert.DeserializeObject<R>(content, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
                }
            }
            catch (Exception)
            {
            }
        }
    }

    public class RequestParamterAttribute : Attribute
    {
        /// <summary>
        /// 相对地址
        /// </summary>
        public string Url { get; set; }

        public HttpMethod Method { get; set; }

        public string[] Headers { get; set; }

        /// <summary>
        /// 当前地址是相对还是全地址,  相对路径为true,默认true 
        /// </summary>
        public bool Relative { get; set; }



        /// <summary>
        /// 发送数据类型 默认为application/json 只有post需要设置此参数
        /// text/html ： HTML格式
        /// text/plain ：纯文本格式      
        /// text/xml ：  XML格式
        /// image/gif ：gif图片格式    
        /// image/jpeg ：jpg图片格式 
        /// image/png：png图片格式
        /// application/xhtml+xml ：XHTML格式
        /// application/xml     ： XML数据格式
        /// application/atom+xml  ：Atom XML聚合格式    
        /// application/json    ： JSON数据格式
        /// application/pdf       ：pdf格式  
        /// application/msword  ： Word文档格式
        /// application/octet-stream ： 二进制流数据（如常见的文件下载）
        /// application/x-www-form-urlencoded :form表单
        /// </summary>
        public string ContentType { get; set; }

        public string Version { get; set; }

        /// <summary>
        /// form-data 分隔符
        /// </summary>
        public string Boundary { get; set; }

        /// <summary>
        /// 备注
        /// </summary>
        public string Remarks { get; set; }

        public RequestParamterAttribute()
        {
            Relative = true;
            ContentType = "application/json";
            Method = HttpMethod.Post;
        }
    }



    [AttributeUsage(AttributeTargets.Property)]
    public class RequestPropertyAttribute : Attribute
    {

        public string Name { get; set; }

        public PropertyType Type { get; set; }
        public RequestPropertyAttribute()
        {
            Name = null;
            Type = PropertyType.Parameter;
        }
    }


    public enum PropertyType
    {
        /// <summary>
        /// 表示此属性是要写到body中的,属性只能是byte[]
        /// </summary>
        Body = 1,
        /// <summary>
        /// 表示此属性会加到URL中
        /// </summary>
        Url = 2,
        /// <summary>
        /// 此属性会加到请求头中
        /// </summary>
        Head = 3,
        /// <summary>
        /// 默认属性
        /// </summary>
        Parameter
    }


    public class FileLogic
    {
        /// <summary>
        /// 为文件添加users，everyone用户组的完全控制权限
        /// </summary>
        /// <param name="filePath"></param>
        public static void AddSecurityControll2File(string filePath)
        {
            FileInfo fileInfo = new FileInfo(filePath);
            //获得该文件的访问权限
            System.Security.AccessControl.FileSecurity fileSecurity = fileInfo.GetAccessControl();
            //添加ereryone用户组的访问权限规则 完全控制权限
            fileSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
            //添加Users用户组的访问权限规则 完全控制权限
            fileSecurity.AddAccessRule(new FileSystemAccessRule("Users", FileSystemRights.FullControl, AccessControlType.Allow));
            //设置访问权限
            fileInfo.SetAccessControl(fileSecurity);
        }

        public static void AddSecurityControll2DirectoryInfo(string filePath)
        {
            DirectoryInfo info = new DirectoryInfo(filePath);
            //获得该文件的访问权限
            System.Security.AccessControl.DirectorySecurity fileSecurity = info.GetAccessControl();
            //添加ereryone用户组的访问权限规则 完全控制权限
            fileSecurity.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
            //添加Users用户组的访问权限规则 完全控制权限
            fileSecurity.AddAccessRule(new FileSystemAccessRule("Users", FileSystemRights.FullControl, AccessControlType.Allow));
            //设置访问权限
            info.SetAccessControl(fileSecurity);
        }


        public static bool GetWebFileToLocal(string httpUrl, string localPath, Action<int, int> callback = null, bool overwrite = true)
        {
            HttpResult<byte[]> head = WebLogic.GetHttpResult<byte[]>(new HttpItem { Method = HttpMethod.Head, Url = httpUrl });
            if (head.Result == null)
                return false;

            int fileLength = head.Result.Length;
            int count = 2048 * 1024;
            int index = -1;
            int loftindex = count;
            using (FileStream fs = System.IO.File.Create(localPath))
            {
                FileLogic.AddSecurityControll2File(localPath);
                do
                {
                    List<string> hreads = new List<string>();
                    loftindex = (index + count >= fileLength) ? fileLength - 1 : index + count;
                    hreads.Add(string.Format("Range: bytes={0}-{1}", index + 1, loftindex));
                    HttpResult<byte[]> hr = WebLogic.GetHttpResult<byte[]>(new HttpItem { Headers = hreads, Method = HttpMethod.Get, Url = httpUrl });
                    if (hr.Success)
                    {
                        fs.Write(hr.Result, 0, hr.Result.Length);
                        index += count;
                        if (callback != null)
                            callback(index + 1, fileLength);
                    }
                } while (index < fileLength);
            }
            return true;
        }


        public static bool DownloadFile(string url, string dest, Action<Point> collback = null)
        {
            if (!Directory.Exists(Path.GetDirectoryName(dest)))
                Directory.CreateDirectory(Path.GetDirectoryName(dest));

            HttpResult<byte[]> head = WebLogic.GetHttpResult<byte[]>(new HttpItem { Method = HttpMethod.Head, Url = url });
            if (head.Result == null)
            {
                System.IO.File.Create(dest);
                return false;
            }
            int fileLength = head.Result.Length;
            int count = 2048 * 1024;
            int index = -1;
            int loftindex = count;
            using (FileStream fs = System.IO.File.Create(dest))
            {
                FileLogic.AddSecurityControll2File(dest);
                do
                {
                    List<string> hreads = new List<string>();
                    loftindex = (index + count >= fileLength) ? fileLength - 1 : index + count;
                    hreads.Add(string.Format("Range: bytes={0}-{1}", index + 1, loftindex));
                    HttpResult<byte[]> hr = WebLogic.GetHttpResult<byte[]>(new HttpItem { Headers = hreads, Method = HttpMethod.Get, Url = url });
                    if (hr.Success)
                    {
                        fs.Write(hr.Result, 0, hr.Result.Length);
                        index += count;
                        collback?.Invoke(new Point(index + 1, (int)fileLength));
                    }
                } while (index < fileLength);
            }
            return true;
        }

        public static AliObjectStorage GetFileMD5(string fileName)
        {
            AliObjectStorage rf = null;
            if (System.IO.File.Exists(fileName))
            {
                rf = new AliObjectStorage();
                byte[] retVal = null;
                using (FileStream fs = new FileStream(fileName, FileMode.Open))
                {
                    rf.Data = new byte[fs.Length];
                    fs.Read(rf.Data, 0, rf.Data.Length);
                }
                MD5 filemd5 = new MD5CryptoServiceProvider();
                retVal = filemd5.ComputeHash(rf.Data);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                    sb.Append(retVal[i].ToString("x2"));
                rf.FileName = sb.ToString();
            }
            return rf;
        }

        public static string GetFileMD5(byte[] buffer)
        {
            MD5 filemd5 = new MD5CryptoServiceProvider();
            byte[] retVal = filemd5.ComputeHash(buffer);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < retVal.Length; i++)
                sb.Append(retVal[i].ToString("x2"));
            return sb.ToString();
        }


        public static byte[] GetFileMD5ByFile(string fileName, out string md5)
        {
            byte[] buffer = null;
            md5 = null;
            if (File.Exists(fileName))
            {
                using (FileStream fs = new FileStream(fileName, FileMode.Open))
                {
                    buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                }
                MD5 filemd5 = new MD5CryptoServiceProvider();
                byte[] retVal = filemd5.ComputeHash(buffer);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                    sb.Append(retVal[i].ToString("x2"));
                md5 = sb.ToString();
            }
            return buffer;
        }

        public static RemoteFile UploadFile(string fileName)
        {
            RemoteFile remoteFile = new RemoteFile();
            string md5 = null;
            byte[] buffer = GetFileMD5ByFile(fileName, out md5);
            remoteFile.md5 = md5;
            remoteFile.url = ServicesAddress.ServerFilePath + remoteFile.md5;

            HttpItem httpItem = new HttpItem { Url = remoteFile.url, Method = HttpMethod.Head };
            HttpResult<string> httpResult = WebLogic.GetHttpResult<string>(httpItem);
            if (!httpResult.Success)
            {
                httpItem = new HttpItem { Url = remoteFile.url, Content = buffer, Method = HttpMethod.Put };
                WebLogic.GetHttpResult<string>(httpItem);
            }
            return remoteFile;
        }
    }
}

